// Borrow<T>, 
// HashMap::get(&self, k: &Q) whre Q: Borrow<str>;

// AsRef<T>
//std::fs::File::open(as_ref<Path>);
// implmentation

//
use std::ops::Mul;
use std::borrow::Borrow;
use std::hash::{Hash, Hasher};

#[derive(Debug, Default, PartialEq)]
struct Int32 (u32);


impl Mul for Int32 {
    type Output = Self;
    fn mul(self, rhs: Self) -> Self::Output {
        Int32(self.0 * rhs.0)
    }
}


#[derive(Debug, Default)]
struct Other32(Int32);
impl Other32 {
    fn new(v: u32) -> Self {
        Self (Int32(v))
    }
}

impl Borrow<Int32> for Other32 {
    fn borrow(&self) -> &Int32 {
        & self.0
    } 
}

#[derive(Debug, Default)]
struct NewOther(Int32);
impl NewOther {
    fn new(v: u32) -> Self {
        Self (Int32(v))
    }
}

impl Borrow<Int32> for NewOther {
    fn borrow(&self) -> &Int32 {
        & self.0
    } 
}


fn call(t:& impl Borrow<Int32>) -> Int32{
    Int32(t.borrow().0 * 2)
}





//trait std::borrow::Borrow<T>
//   Eq, Ord, Hash is the same impl for Object and T; if  Object.borrow()->&T  
//
// 1.1 impl the Eq & PartialEq trait 
#[derive(Default, Debug)]
struct EqObject {
    x: i32,
    y: f32,
    z: String,
}
impl EqObject {
    fn new(x: i32, y: f32, z: &str) -> Self {
        Self {
            x,
            y,
            z: z.to_owned(),
        }
    }
}

impl PartialEq for EqObject {
    fn eq(&self, other: &Self) -> bool {
        self.x == other.x && self.y == other.y && self.z == other.z
    }
}
// Note trait Eq is blank Trait for sign
impl Eq for EqObject {}

//1.2 impl the trait Hash

impl Hash for EqObject {
    fn hash<H>(&self, state: &mut H) 
    where H: Hasher
    {
        self.x.hash(state);
        (self.y as i32).hash(state);
        self.z.hash(state);
    }
}




#[derive(Debug, Default, Eq, PartialEq)]
struct SameEqObject {
    inner: EqObject,
}
impl SameEqObject {
    fn new(x: i32, y: f32, z: &str) -> Self {
        Self {
            inner: EqObject::new(x, y, z),
        }
    }
}
impl Hash for SameEqObject {
    fn hash<H>(&self, state: &mut H) 
    where H: Hasher
    {
        self.inner.hash(state);
    }
}


impl Borrow<EqObject> for SameEqObject {
    fn borrow(&self) -> &EqObject {
        & self.inner
    }
}

// HashMap::get(&self, k: &Q) -> bool 
// where K: Borrow<Q>, 
// Q: Hash + Eq + ?Sized
// {
//      self.iter().find( |pk| -> pk.borrow() == k) 
// }
//
// e.g. HashMap<SameEqObject, String>;
// so K-> SameEqObject, Q -> EqObject
//
// SameEqObject.borrow() -> &EqObject




//AsRef<T> 
// simple refer for objects;  no failed impl; 
// a method for Overrides

impl AsRef<EqObject> for SameEqObject {
    fn as_ref(&self) -> &EqObject {
        println!("SameEqObject impl for as_ref:");
        & self.inner
    }
}

impl AsRef<EqObject> for EqObject {
    fn as_ref(&self) -> &EqObject {
        println!("EqObject impl for as_ref:");
        self
    }
}
fn as_ref_call<T>(obj:& T)
where T: AsRef<EqObject>
//fn as_ref_call<T: AsRef<EqObject>>(obj:& T)
//fn as_ref_call(obj:& impl AsRef<EqObject>)
{
    println!("{:?}", obj.as_ref())
}





//TODO: std::fs::File::open(path: AsRef<Path>);
// impl AsRef<Path> for Path
// impl asref<path> for string;
// Path is slice impl , for more reading of source code



#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_as_ref(){
        as_ref_call(&SameEqObject::default());
        as_ref_call(&EqObject::default());
    }

    #[test]
    fn test_eq(){
        let a = EqObject::default();
        assert_eq!(a, EqObject::default());

        let a = EqObject::new(1, 1.2, "hello");
        assert_eq!(a, EqObject::new(1, 1.2, "hello"));
    }

    use std::collections::HashMap;
    #[test]
    fn test_hash_custom(){
        let mut map = HashMap::new();
        
        map.insert(SameEqObject::default(), "hello".to_owned());
        assert!(map.get(&EqObject::default()).is_some());

        map.insert(SameEqObject::new(1, 1.2, "hi"), "world".to_owned());
        assert!(map.get(&EqObject::new(1, 1.2, "hi")).is_some());

        assert!(map.get(&EqObject::new(2, 3.0, "w")).is_none());

        println!("map:{:?}", map);
    }
    
    #[test]
    fn test_borrow_with_int32(){
        assert_eq!(Int32(6), Int32(3) * Int32(2));
        assert_eq!(Int32(3333), Other32::new(3333).0);
        assert_eq!(Int32(6666), Other32::new(6666).0);


        assert_eq!(Int32(222), call(&Int32(111)));
        assert_eq!(Int32(222), call(&Other32::new(111)));
        assert_eq!(Int32(222), call(&NewOther::new(111)));
    }
}
